﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ContractDriven
{
    #region Assert Comparer
    internal class AssertComparer<T> : IComparer<T>
    {
        #region Static methods
        public static IComparer<T> GetComparer()
        {
            return new AssertComparer<T>();
        }
        #endregion

        public int Compare(T x, T y)
        {
            Type type = typeof(T);
            #region Null
            // Null?
            if (!type.IsValueType || (type.IsGenericType && type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Nullable<>))))
            {
                if (Equals(x, default(T)))
                {
                    if (Equals(y, default(T)))
                        return 0;
                    return -1;
                }

                if (Equals(y, default(T)))
                    return -1;
            }
            #endregion
            #region Same Type
            // Same type? if not the same type then fail them
            if (x.GetType() != y.GetType())
                return -1;
            #endregion
            #region Arrays
            // Arrays?
            if (x.GetType().IsArray)
                return CompareArrays(x as Array, y as Array);

            #endregion
            #region IComarable<T>
            if (ImplementsIComparable(x, y))
                return CompareIComparable(x, y);
            #endregion
            #region IEquatable<T>
            // Implements IEquatable<T>?
            IEquatable<T> equatable = x as IEquatable<T>;

            if (equatable != null)
                return equatable.Equals(y) ? 0 : -1;
            #endregion

            // Last case, rely on Object.Equals
            return Equals(x, y) ? 0 : -1;
        }

        #region Compare Arrays
        private static int CompareArrays(Array xArray, Array yArray)
        {
            Contract.Require(Assert.IsNotNull(xArray));
            Contract.Require(Assert.IsNotNull(yArray));
            //Multi-dimension array comparison is not supported
            Contract.Require(Assert.Equal<int>(1, xArray.Rank));


            if (xArray.Length != yArray.Length)
                return -1;

            for (int index = 0; index < xArray.Length; index++)
                if (!Equals(xArray.GetValue(index), yArray.GetValue(index)))
                    return -1;

            return 0;

        }
        #endregion

        private static bool ImplementsIComparable(T x, T y)
        {
            // Implements IComparable<T>?
            IComparable<T> comparable1 = x as IComparable<T>;

            if (comparable1 != null)
                return true;

            // Implements IComparable?
            IComparable comparable2 = y as IComparable;

            if (comparable2 != null)
                return true;

            return false;
        }

        private static int CompareIComparable(T x, T y)
        {
            // Implements IComparable<T>?
            IComparable<T> comparable1 = x as IComparable<T>;

            if (comparable1 != null)
                return comparable1.CompareTo(y);

            // Implements IComparable?
            IComparable comparable2 = x as IComparable;

            if (comparable2 != null)
                return comparable2.CompareTo(y);

            return -1;
        }
    }

    #endregion
}
